﻿using CommonTools;
using FSUIPC;
using System;
using System.Collections;
using System.Configuration;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows;

namespace SimWithP3d.Vehicle
{
    public abstract class VehicleDevice
    {
        System.Timers.Timer AIRefreshTimer;
        public VehicleDevice()
        {
            ConnectFsuipc();
            AIRefreshTimer = new System.Timers.Timer();
            AIRefreshTimer.Interval = 1000;
            AIRefreshTimer.Elapsed += AIRefreshTimer_Elapsed;
            AIRefreshTimer.Start();
        }

        private void AIRefreshTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                if (AI == null)
                {
                    AI = FSUIPCConnection.AITrafficServices;
                }
                AI.RefreshAITrafficInformation(true, true);
            }
            catch
            {
            }
        }
        #region 公共offset
        public AITrafficServices AI;



        #region Fuel Tank Setting Offset  _p 百分比  _v最大容量
        public Offset<int> EmptyWeight = new Offset<int>("map", 0x1330);   //  value/256 = value
        public Offset<int> ZeroFuelWeight = new Offset<int>("map", 0x3BFC); // value/256 = tvalue
        public Offset<short> FuelWeight = new Offset<short>("map", 0x126C); //  /256;
        #endregion

        #region Failure Setting Offset
        //Engines Failures   
        public Offset<short> Engine_numbers = new Offset<short>("map", 0x0AEC);   //  Number of Engines  发动机的数量
        public Offset<BitArray> Engine_fail_Offset = new Offset<BitArray>("ENGINE_FAIL", 0x0B6B, 1);       //Engine bit0 Eng1, bit1 Eng2, bit2 Eng3, bit3 Eng4
        public Offset<BitArray> Engine_Fire_offset = new Offset<BitArray>("ENGINE_FIRE", 0x3366, 1);      //Engine fire tag, bit0 Eng1, bit1 Eng2, bit2 Eng3, bit3 Eng4

        //condition
        public Offset<int> AIRSPEED = new Offset<int>("CONDITION", 0x02BC);//空速表

        // Gauge Failures
        public Offset<byte> ASIgauge = new Offset<byte>("GAUGE", 0x0B65);     //Airspeed Indicator gauge
        public Offset<byte> ALTgauge = new Offset<byte>("GAUGE", 0x0B66);     //Altimeter gauge
        public Offset<byte> AIgauge = new Offset<byte>("GAUGE", 0x0B67);      //Attitude Indicator gauge
        public Offset<byte> VSIgauge = new Offset<byte>("GAUGE", 0x0B6E);         //Verticalspeed Indicator gauge
        public Offset<byte> Maggauge = new Offset<byte>("GAUGE", 0x0B69);          //MagCompass
        public Offset<byte> DirectionInd = new Offset<byte>("GAUGE", 0x0B6D);    //Direction indicator
        public Offset<byte> Turngauge = new Offset<byte>("GAUGE", 0x0B72);        //Turn coordinator gauge

        //System Failures  ---------------failure = 1 ,0 OK.
        public Offset<int> GeneratorENG1 = new Offset<int>("SYSTEM", 0x3B78);      //ENG1 Generator  0 failure, 1 OK
        public Offset<int> GeneratorENG2 = new Offset<int>("SYSTEM", 0x3ABC8);      //ENG2 Generator  0 failure, 1 OK
        public Offset<int> GeneratorENG3 = new Offset<int>("SYSTEM", 0x39F8);      //ENG3 Generator  0 failure, 1 OK
        public Offset<int> GeneratorENG4 = new Offset<int>("SYSTEM", 0x3938);      //ENG4 Generator  0 failure, 1 OK
        public Offset<byte> Hydraulic = new Offset<byte>("SYSTEM", 0x0B62);        //Hydraulics failure
        public Offset<byte> Electrical = new Offset<byte>("SYSTEM", 0x0B6A);             //Electrics
        public Offset<byte> Vacuumgauge = new Offset<byte>("SYSTEM", 0x0B73);      //Vacuum gauge
        public Offset<byte> Pitot = new Offset<byte>("SYSTEM", 0x0B71);            //Pitot inoperable

        //FUEL Failure
        public Offset<byte> FuelInd = new Offset<byte>("FUEL", 0x0B6C);      //Fuel indicators
        public Offset<int> left_Main = new Offset<int>("FUEL", 0x0B7C);//left main tank 
        public Offset<int> left_Main_total = new Offset<int>("FUEL", 0x0B80);//left main tank total
        public Offset<int> left_Aux = new Offset<int>("FUEL", 0x0B84);//left aux tank
        public Offset<int> left_Aux_total = new Offset<int>("FUEL", 0x0B88);//left aux tank total
        public Offset<int> Left_Wing_Tip = new Offset<int>("FUEL", 0x0B8C);//Left_Wing_Tip tank
        public Offset<int> Left_Wing_Tip_total = new Offset<int>("FUEL", 0x0B90);//Left_Wing_Tip tank total
        public Offset<int> center1 = new Offset<int>("FUEL", 0x0B74);//center1 tank
        public Offset<int> center1_total = new Offset<int>("FUEL", 0x0B78);//center1 tank total
        public Offset<int> center2 = new Offset<int>("FUEL", 0x1244);//center2 tank
        public Offset<int> center2_total = new Offset<int>("FUEL", 0x1248);//center2 tank total
        public Offset<int> center3 = new Offset<int>("FUEL", 0x124C);//center3 tank
        public Offset<int> center3_total = new Offset<int>("FUEL", 0x1250);//center3 tank total
        public Offset<int> right_Main = new Offset<int>("FUEL", 0x0B94);//right main tank 
        public Offset<int> right_Main_total = new Offset<int>("FUEL", 0x0B98);//right main tank total
        public Offset<int> right_Aux = new Offset<int>("FUEL", 0x0B9C);//right aux tank
        public Offset<int> right_Aux_total = new Offset<int>("FUEL", 0x0BA0);//right aux tank total
        public Offset<int> right_Wing_Tip = new Offset<int>("FUEL", 0x0BA4);//right_Wing_Tip tank
        public Offset<int> right_Wing_Tip_total = new Offset<int>("FUEL", 0x0BA8);//right_Wing_Tip tank total
        public Offset<int> external1 = new Offset<int>("FUEL", 0x1254);//external 1  tank
        public Offset<int> external1_total = new Offset<int>("FUEL", 0x1258);//external 1  tank total
        public Offset<int> external2 = new Offset<int>("FUEL", 0x125C);//external 2  tank
        public Offset<int> external2_total = new Offset<int>("FUEL", 0x1260);//external 2  tank total

        //Gear&Flap Failure
        public Offset<int> Gear = new Offset<int>("GEAR", 0x0BE8);//Gear Up --0  own--16383
        #endregion

        #region Time and Season Setting
        public Offset<byte> HourSim = new Offset<byte>("Time", 0x0238);          //Hour of local time in SIM
        public Offset<byte> MinSim = new Offset<byte>("Time", 0x0239);           //Minute of local time in SIM
        public Offset<byte> SecSim = new Offset<byte>("Time", 0x023A);           //Second of local time in SIM           lose efficacy
        public Offset<short> DayNumber = new Offset<short>("Time", 0x023E);
        public Offset<short> YearNumber = new Offset<short>("Time", 0x0240);
        #endregion



        #region Location Setting
        public Offset<int> sendControl = new Offset<int>("Location", 0x3110, true); // 演示移动飞机


        public Offset<short> SlewRoll = new Offset<short>("Location", 0x05E4);
        public Offset<short> SlewYaw = new Offset<short>("Location", 0x05E6);
        public Offset<short> SlewPitch = new Offset<short>("Location", 0x05EE);
        public Offset<short> SlewUp = new Offset<short>("Location", 0x05E8);
        public Offset<short> SlewForward = new Offset<short>("Location", 0x05EB);
        public Offset<short> SlewLeft = new Offset<short>("Location", 0x05ED);


        public Offset<int> Altitude = new Offset<int>("Location", 0x0574);                       //飞机高度
        public Offset<int> Pushback = new Offset<int>("Location", 0x31F4);                       //推出 0 back 1 left  2 right 3 off
        public Offset<int> Heading = new Offset<int>("Location", 0x0580);
        public Offset<int> Bank = new Offset<int>("Location", 0x057C);               //  *360/65536/65536 = value
        public Offset<int> Pitch = new Offset<int>("Location", 0x0578);
        public Offset<int> Speed = new Offset<int>("Location", 0x0558);

        public Offset<FsLongitude> playerLon = new Offset<FsLongitude>("map", 0x0568, 8);
        public Offset<FsLatitude> playerLat = new Offset<FsLatitude>("map", 0x0560, 8);
        public Offset<short> Aoa = new Offset<short>("map", 0x11BE);            //Angle of attack
        public Offset<int> TAS = new Offset<int>("map", 0x02B8);                //  /128 = value
        public Offset<int> IAS = new Offset<int>("map", 0x02BC);                //  /128 = value
        public Offset<int> VS = new Offset<int>("map", 0x02C8);                 //  /256 = value (m/s)    *60*3.28084/256 = value(ft/min)
        public Offset<double> GForce = new Offset<double>("map", 0x1140);       //  /624 
        public Offset<int> pres_ALTIMETER_map = new Offset<int>("map", 0x3324);////气压高度表，用于高度表示数
        public Offset<int> Pitch_A = new Offset<int>("map", 0x0578);          //*360/(65536*65536)   dgree    下加上减  左加右减
        public Offset<int> Bank_A = new Offset<int>("map", 0x057C);
        public Offset<uint> Heading_A = new Offset<uint>("map", 0x0580);

        #endregion

        #region Simulator status
        public Offset<short> PauseSim = new Offset<short>("Freeze", 0x0262);       //simulator pause setting
        public Offset<short> SlewMode = new Offset<short>("Location", 0x05DC);
        #endregion
        #endregion

        #region 故障状态保存
        public bool[] Induicator_flag = new bool[15];//仪表故障标志  fasle 仪表可用  true 仪表不可用
                                                     //0-垂直速度表
                                                     //1-空速表
                                                     //2-转速表
                                                     //3-姿态仪
                                                     //4-高度表
                                                     //5-MANIFOLD_PRESSURE_GAGE
                                                     //6-CAUTION化油器温度表
                                                     //7-AMPS电表
                                                     //8-OIL PRESSURE油压表
                                                     //9-辅助燃油量表AUX FUEL
                                                     //10-OIL TEMP燃油温度表
                                                     //11-主燃油表
                                                     //12-汽缸盖温度表
                                                     //13-磁罗盘   
        #endregion

        #region FSUIPC 连接和刷新方法
        public bool ConnectFsuipc()
        {
            if (FSUIPCConnection.IsOpen == false)
            {
                try
                {
                    FSUIPCConnection.Close();
                    FSUIPCConnection.Open();
                    return true;
                }
                catch (Exception ex)
                {
                    App.RecordErrorLog(ex);
                    return false;
                }
            }
            else
            {
                return true;
            }
        }

        public bool DisconnectFsuipc()
        {
            try
            {
                FSUIPCConnection.Close();
                AI = FSUIPCConnection.AITrafficServices;
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public void UpdateValue()
        {
            try
            {
                FSUIPCConnection.Process();
            }
            catch
            {
                ConnectFsuipc();
            }
        }
        public bool UpdateValue(String a)
        {
            try
            {
                if (FSUIPCConnection.IsOpen)
                {
                    FSUIPCConnection.Process(a);
                    return true;
                }
                else
                {
                    if (Process.GetProcessesByName("Prepar3D").Length != 0)
                    {
                        ConnectFsuipc();
                    }
                    return false;
                }
            }
            catch
            {
                ConnectFsuipc();
                return false;
            }
        }
        /// <summary>
        ///  动态偏移量设置数据
        /// </summary>
        public void SwapDataSet()
        {

        }
        /// <summary>
        /// 动态偏移量获取数据
        /// </summary>
        public void SwapDataGet()
        {

        }
        #endregion

        #region 实例UDP 及接收线程
        public abstract ThreadStart ReceiveMessage();
        /// <summary>
        /// 下位机IPEndpoint地址
        /// </summary>
        protected static IPEndPoint lowerDevicesIPEndPoint = CommonTool.CvStr2IPEndPoint(ConfigurationManager.AppSettings["lowerDevicesIPEndPoint"]);
        /// <summary>
        /// 运行平台控制软件IPEndpoint地址
        /// </summary>
        protected static IPEndPoint motionPlatformIPEndPoint = CommonTool.CvStr2IPEndPoint(ConfigurationManager.AppSettings["motionPlatformIPEndPoint"]);
        /// <summary>
        /// 机型驱动UDP地址
        /// </summary>
        protected static UdpClient vehicleDeviceUdpClient = null;
        public bool Debugmode = false;
        /// <summary>
        /// udp服务器初始化，开启监听线程
        /// </summary>
        public void UdpInit(ThreadStart ReceiveMessage)
        {
            try
            {
                if (vehicleDeviceUdpClient != null)
                    return;
                IPEndPoint locatePoint = CommonTool.CvStr2IPEndPoint(ConfigurationManager.AppSettings["vehicleDeviceIPEndPoint"]);
                vehicleDeviceUdpClient = new UdpClient(locatePoint);
                Thread udpReceive1 = new Thread(ReceiveMessage);
                udpReceive1.IsBackground = true;
                udpReceive1.Start();
                Console.WriteLine("服务器已开启");
            }
            catch (Exception ex)
            {
                MessageBox.Show("机型驱动IP地址错误或端口被占用！确认后将关闭程序。");
                Application.Current.Shutdown();
            }
        }
        /// <summary>
        /// 发送字节数组到下位机
        /// </summary>
        /// <param name="data"></param>
        public void SendData(int areaCode, int mark, byte[] last6, byte dataLength = 0x08)
        {
            byte[] data = CreatSendBytes(areaCode, mark, last6, dataLength);
            vehicleDeviceUdpClient.Send(data, data.Length, lowerDevicesIPEndPoint);
        }
        /// <summary>
        /// 将发送数据转换成byte[]
        /// </summary>
        /// <param name="areaCode">区域编号占3个字节</param>
        /// <param name="mark">代号占2个字节</param>
        /// <param name="pozition">位置点1个字节</param>
        /// <param name="functionCode">功能码占1个字节</param>
        /// <param name="undetermined">目前未定义占4个字节，默认OX00000000</param>
        /// <param name="dataLength">数据长度占一个字节，默认长度为OX08，mark+pozition+functionCode+undetermined=8</param>
        /// <returns></returns>
        protected byte[] CreatSendBytes(int areaCode, int mark, byte[] last6, byte dataLength = 0x08)
        {
            byte[] tmp = new byte[12];
            //区域码
            tmp[0] = (byte)((areaCode << 8 & 0xff0000) >> 16);
            tmp[1] = (byte)((areaCode << 8 & 0xff00) >> 8);
            tmp[2] = (byte)((areaCode << 8 & 0xff));
            //数据长度
            tmp[3] = dataLength;
            //代号
            IntToHexByte(mark).CopyTo(tmp, 4);
            //后6位
            last6.CopyTo(tmp, 6);
            return tmp;
        }
        #endregion

        #region 处理数据方法
        /// <summary>
        /// 将INT转成3个字节
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        protected byte[] GetByteByPoint(int point)
        {
            byte[] array = new byte[3];
            array[0] = Convert.ToByte((point & 0xff0000) >> 16);
            array[1] = Convert.ToByte((point & 0xff00) >> 8);
            array[2] = Convert.ToByte(point & 0xff);
            return array;
        }
        /// <summary>
        /// 根据协议解析成相应协议类型
        /// </summary>
        public void ParseBytesToStr(byte[] data, out int nodeId, out int mark)
        {
            //区域编号
            nodeId = 0;
            nodeId |= data[1];
            //mark
            byte[] markBytes = new byte[2];
            Array.ConstrainedCopy(data, 4, markBytes, 0, 2);
            mark = ByteToHexStr(markBytes);
        }
        /// <summary>
        /// 字符串转16进制字节数组
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public byte[] IntToHexByte(int num)
        {
            string hexString = num.ToString("x2").Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString = hexString.Insert(0, 0.ToString()); //如果长度为奇，在开头补零
            byte[] returnBytes = new byte[hexString.Length / 2];//byte[]数组
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);//每两个字符转byte,放入数组元素中
            return returnBytes;
        }
        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public int ByteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                }
            }
            return int.Parse(returnStr, System.Globalization.NumberStyles.HexNumber);
        }
        /// <summary>
        /// 燃油减少函数
        /// </summary>
        /// <param name="fuel"></param>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public int Fuel_leak(string fuel, int value1)
        {
            int sum = (int)(value1 - (Convert.ToInt32(fuel) * 65536 * 1.28));
            if (sum >= 0)
                return sum;
            else
                return 0;
        }

        #endregion
    }
}
